iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
Modern Web

每天一點 API:打造我的生活小工具系列 第 21

Day 21 — API Key 入門:安全存放與實作 OWM 天氣查詢

  • 分享至 

  • xImage
  •  

前幾天我們已經學會抓 API 資料並進行分析,但很多實際應用中的 API 都需要金鑰(API Key)。

因此,今天我要挑戰的是申請一把 API Key ,並學會安全管理避免金鑰外洩。

為什麼需要 API Key?

API Key 是一組像密碼一樣的字串,用來辨識每個使用 API 的人。

需要 API Key 的原因:

API 金鑰的作用就是要先確認使用的人有註冊過,這樣可以避免陌生人亂用服務。它還能限制每個人使用的次數或速度,避免有人一直狂用把系統弄壞。另外,平台也能透過金鑰記錄大家的使用情況,方便做用量統計或收費。

今天的小專案目標

今天我的目標是成功申請並啟動 OpenWeatherMap (簡稱 OWM)的 API 金鑰,接著學習如何安全地存放金鑰而不是直接寫在程式碼裡,然後用 Python 呼叫 OWM 的天氣服務,顯示自己選擇的城市的溫度和天氣情況,同時也要了解金鑰管理的重要性,避免發生錯誤。

實作流程

步驟 0:環境準備

pip install requests python-dotenv

用 Python 的 requests 套件可以幫助我們發送像瀏覽器一樣的網路請求,讓我們向網站或 API 取得資料。

python-dotenv 這個工具可以幫我們從一個叫做 .env 的檔案中,安全讀取我們儲存的秘密資訊,比如 API 金鑰。讓程式在執行時,不用把金鑰直接寫在程式碼裡。

步驟 1:註冊並拿到 API 金鑰

  • 到 OpenWeatherMap 網站註冊帳號。
    https://ithelp.ithome.com.tw/upload/images/20251003/20178708KIha3s8HsT.png

  • 登入後去 API keys 頁面,複製預設的金鑰。
    https://ithelp.ithome.com.tw/upload/images/20251003/20178708QHUYsKOc0r.png

  • 需要先等幾分鐘,才能讓金鑰正式啟用。

步驟 2:安全存放金鑰

在專案資料夾裡,新增一個叫做 .env 的檔案。

把金鑰寫成這樣:

OWM_API_KEY=我的金鑰

我們需要在 .gitignore 檔案裡加上 .env,這樣才不會把金鑰上傳到 GitHub。

.env

程式裡用 os.environ.get("OWM_API_KEY") 讀金鑰,不能直接在程式碼寫金鑰。

步驟 3:練習一個測試金鑰的程式

用 API 查詢台北市的天氣。

把地點、溫度和天氣狀況印出來。

import os
import requests

API = "https://api.openweathermap.org/data/2.5/weather" 

定義一個常數 API:OpenWeatherMap 的現在天氣端點網址。

def main():
    api_key = os.environ.get("OWM_API_KEY")  # 從環境變數取金鑰
    if not api_key:
        print("找不到 OWM_API_KEY。請在 .env 或系統環境變數設定。")
        return

    params = { # 要送到 API 的查詢參數
        "q": "Taipei",     # 城市名(也可用 lat/lon)
        "appid": api_key,  # 金鑰
        "units": "metric", # 攝氏
        "lang": "zh_tw",   # 回傳中文描述
    }
    try:
        r = requests.get(API, params=params, timeout=10)
        r.raise_for_status()
        data = r.json()
    except requests.exceptions.RequestException as e:
        print("API 失敗:", e)
        return

    # 取出常見欄位
    name = data.get("name")
    main = (data.get("main") or {})
    weather0 = (data.get("weather") or [{}])[0]
    temp = main.get("temp")
    desc = weather0.get("description")

    print(f" {name} |  {temp}°C |  {desc}")

if __name__ == "__main__":
    main()

執行結果:

Taipei 現在 29.84°C,晴

步驟 4:做更安全的程式

使用 dotenv.load_dotenv().env 載入金鑰並做好錯誤處理,且在印出結果時只顯示遮蔽後的金鑰以避免洩漏。

import os
import requests
from dotenv import load_dotenv

API = "https://api.openweathermap.org/data/2.5/weather"
def mask(key: str) -> str:
    if not key: return ""
    if len(key) <= 6: return "*" * len(key)
    return key[:3] + "*" * (len(key)-6) + key[-3:]

def get_weather_by_city(city: str, units="metric", lang="zh_tw", timeout=10):
    load_dotenv()  # 讀取 .env(若有)
    api_key = os.environ.get("OWM_API_KEY")
    if not api_key:
        raise RuntimeError("找不到 OWM_API_KEY(請在 .env 或環境變數設定)")

mask():把金鑰遮罩,避免把完整金鑰印出來
get_weather_by_city():讀 .env 拿金鑰,沒有就報錯

    params = { # #要送到 API 的查詢參數
        "q": city,
        "appid": api_key,
        "units": units,
        "lang": lang,
    }
    try:
        r = requests.get(API, params=params, timeout=timeout)
        # 常見錯誤處理
        if r.status_code == 401:
            raise RuntimeError("401 未授權:API Key 無效或未啟用。")
        if r.status_code == 404:
            raise RuntimeError(f"404 找不到城市:{city}")
        if r.status_code == 429:
            raise RuntimeError("429 達到呼叫上限,請稍後再試。")
        r.raise_for_status()
        return r.json()
    except requests.exceptions.Timeout:
        raise RuntimeError("連線逾時,網路可能不穩或伺服器忙碌。")
    except requests.exceptions.ConnectionError:
        raise RuntimeError("連線錯誤,請檢查網路或防火牆設定。")
    except requests.exceptions.HTTPError as e:
        raise RuntimeError(f"HTTP 錯誤:{e}")
def format_weather(data: dict) -> str:
    name = data.get("name") or "-"
    main = (data.get("main") or {})
    weather0 = (data.get("weather") or [{}])[0]
    temp = main.get("temp")
    feels = main.get("feels_like")
    desc = weather0.get("description") or "-"
    return f" {name}\n 氣溫:{temp}°C(體感 {feels}°C)\n 天氣:{desc}"

if __name__ == "__main__":
    city = "Taipei"
    try:
        data = get_weather_by_city(city)
        print(format_weather(data))
    except RuntimeError as e:
        # 不要在錯誤訊息中印出完整金鑰
        key = os.environ.get("OWM_API_KEY")
        print("發生錯誤:", e)
        if key:
            print("(提示)目前使用的金鑰:", mask(key))

執行結果:

Taipei
 氣溫:29.56°C(體感 33.99°C)
 天氣:晴,少雲

API 金鑰管理小技巧

不要直接寫在程式碼裡

把 API 金鑰直接寫在程式碼中很危險,因為程式碼可能會被別人看到。安全做法是把金鑰放在專門的設定檔,像是 .env 文件裡,再讓程式讀取。

使用 .gitignore 保護金鑰

如果我們用 Git 來管理程式碼,要在 .gitignore 檔裡寫上 .env,這樣這個檔案就不會被推送到 GitHub,避免別人看到金鑰。

不要把金鑰放在前端程式裡

前端像是 JavaScript 或 HTML 的程式碼是開放給使用者看到的,不能放敏感的金鑰。API 金鑰應該只放在後端程式中,確保安全。

定期更換金鑰

為了減少風險,我們最好定期把 API 金鑰換掉。這樣如果有人偷到我們的金鑰,它也不會一直有效。

金鑰外洩時要快速處理

如果不小心把金鑰外洩了,要馬上到服務平台撤銷那個金鑰,然後換一組新的,防止別人濫用我們的 API。

常見錯誤與解決方法

401 Unauthorized(未授權)

這表示輸入的 API 金鑰錯誤或還沒啟用。要先檢查金鑰是不是打錯字,或者剛申請的金鑰還沒生效,等幾分鐘後再試。

404 Not Found(找不到資源)

這通常是因為輸入的城市名稱拼錯了。可以改用更穩定的經緯度(latitude、longitude)來查詢天氣,這樣比較不會出錯。

429 Too Many Requests(請求過多)

表示對 API 的使用次數太多,超出平台限制。解決方法是減少呼叫頻率,或是將取得的資料暫存(快取)起來,避免重複請求。

Timeout / Connection Error(連線逾時或錯誤)

代表網路不穩或伺服器太忙沒有回應。可以在程式設定等待時間,遇到這種錯誤時重試幾次,避免程式直接崩潰。

今日總結

今天我完成了申請 OpenWeatherMap 的 API Key,並學會如何把金鑰安全地存放與在程式中正確呼叫,同時更加理解金鑰管理的重要性與常見錯誤的成因與處理方式。未來使用其他需要金鑰的 API 時,我也能沿用這套流程,降低風險並提升開發效率。


上一篇
Day20 — 從抓資料到畫圖表:API 小專案實戰
下一篇
Day 22 — HTTP 標頭、快取與 CORS 一次搞懂
系列文
每天一點 API:打造我的生活小工具23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
shenra
iT邦新手 5 級 ‧ 2025-10-04 07:07:35

一個簡短的小專案,將API key 使用展現的很清楚。喜歡對例外處理的方式,非常棒。謝謝

我要留言

立即登入留言